import math
import sys
import asyncio

import socket, random
import os, time, logging
import re, requests, json

import platform

from lib import config
import chardet
import configparser
import psutil
import concurrent.futures
from diskcache import Cache

logging_path = config.logging_path
# logging.basicConfig(filename='bd_logger.log', filemode='a', format='%(name)s - %(levelname)s - %(message)s', directory=logging_path)
loggin_file = 'bd_logger'
logger = logging.getLogger(loggin_file)
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
# 将句柄添加到日志记录器
logger.addHandler(console_handler)
# 创建一个文件句柄，将日志输出到文件
t = time.strftime('%Y%m%d', time.localtime(time.time()))
file_handler = logging.FileHandler(f"{logging_path}/bd_logger.log")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# 将句柄添加到日志记录器
logger.addHandler(file_handler)

TN = ['site888_3_pg', 'sitehao123_15', '50000049_hao_pg', '02049043_6_pg', '77092190_pg', 'request_28_pg',
      'site5566',
      '58059073_pg', '56060048_4_pg', 'baijuyi_pg', 'windowstime_pg', '21002492_17_hao_pg', '50000049_hao_pg']

FBL = [{'width': 1600, 'height': 1200}, {'width': 1280, 'height': 1024}, {'width': 1152, 'height': 864},
       {'width': 1024, 'height': 768}, {'width': 1920, 'height': 1080},
       {'width': 1768, 'height': 992}, {'width': 1680, 'height': 1050}, {'width': 1440, 'height': 900},
       {'width': 1280, 'height': 1024}, {'width': 1280, 'height': 960},
       {'width': 1280, 'height': 720}, {'width': 1176, 'height': 664}, {'width': 1536, 'height': 864}]


async def rand_ua():
    with open('../common/ua.txt', 'r') as f:
        ua_list = f.read().splitlines()
    return random.choice(ua_list)


def rand_cookie():
    with open('../common/cookies.txt', 'r') as f:
        cookie_list = f.read().splitlines()
    return random.choice(cookie_list)


def randomTime(x=0, y=0):
    if x == 0 and y == 0:
        return random.choice(range(10, 40))
    else:
        return random.choice(range(x, y))


def TestPingStatus(proxyIp, url=''):
    if url == '':
        url = "https://www.baidu.com"
    command = f"curl -sL -w %{{http_code}} --connect-timeout 15  -o /dev/null {url} --proxy http://{proxyIp}"
    print(command)
    # result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True)
    result = os.popen(command)
    # print(f"{i}. >>> http code:{result.stdout}")
    status = result.readlines()[0]
    if status == '200':
        print(f"protxyIp {proxyIp} ping status ok: {status}")
        return True
    else:
        print(f"ping status fail: {status},url:{url}")
        return False


def _check_port(port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex(('127.0.0.1', port))
    if result == 0:
        # print(f"Port {port} is in use.")
        sock.close()
        return 'in_use'
    else:
        # print(f"port {port} is start...")
        return 'not_use'


def getSocketPort():
    for p in range(11000, 11200):
        if _check_port(p) == 'not_use':
            return p
    # sysPort = 11000
    # killPort(sysPort)
    # print(f"port {sysPort} is start...")
    # return sysPort


def rmdirectory(directory):
    if os.path.exists(directory):
        os.rmdir(directory)


# 取机端口号
def get_random_process_id():
    current_pid = os.getpid()
    random_num = random.randint(11000, 15000)
    random_port = current_pid + random_num
    print(f"生成端口号:{random_port}")
    return random_port


def killPort(sysPort):
    print(f"清除端口{sysPort}对应的进程......")
    pid = 0
    if platform.system() == "Windows":
        command = f'netstat -ano | findstr {sysPort}'
        command_read = os.popen(command).read()
        if command_read:
            pid = os.popen(command).read().split('\n')[0].split()[-1]
        else:
            return
    else:
        command = f"lsof -i:{sysPort} -F -n | grep 'p'"
        command_read = os.popen(command).read()

        if command_read:
            output = os.popen(command).read().split('\n')[0]
            pid = output[1:]
        else:
            return

    print(f"pid = {pid}")
    # print(pid, type(pid))

    if not re.search(r'(\d+)', pid):
        print(f"未找到端口{sysPort}对应的进程")
        return
    else:
        pid = int(pid)

    try:
        if platform.system() == "Windows":
            command = 'taskkill /f /t /pid %s' % pid
        else:
            command = f'kill -9 {pid}'

        os.system(command)
        print(f'{command} ......')
    except Exception as e:
        print(f'关闭chrome进程失败,error:{e}')


def killAllPort():
    for p in range(11000, 11100):
        killPort(p)


def get_memory_info():
    mem_info = psutil.virtual_memory()
    total_mem = mem_info.total
    used_mem = mem_info.used
    available_mem = mem_info.available
    return total_mem, used_mem, available_mem


# 滚动浏览
async def rand_scroll(page):
    try:
        page_height = await page.evaluate('return window.innerHeight')
        # until.logger.info(f"计算页面的高度{page_height}")
        rand_height = random.choice(range(1, int(page_height))) // 2
        logger.info(f'开始进行页面滚动浏览')
        for y in range(0, rand_height, 10):
            await page.evaluate('window.scrollTo(0,%d)' % y)
            await asyncio.sleep(random.choice(range(1, 3)))
    except:
        stopTime = random.choice(range(100, 250))
        topTime = await asyncio.sleep(stopTime / 10)
        logger.info(f'增加停留时间: {stopTime}s')


# 偏移元素
async def element_offset_position(page, element):
    await page.evaluate("arguments[0].scrollIntoView(false);", element)
    await asyncio.sleep(1)
    sc_h = await page.evaluate("return document.documentElement.scrollTop")
    try:
        x = element.location['x'] + random.choice(range(1, int(element.size['width'])))
        y = element.location['y'] + random.choice(range(1, int(element.size['height']))) - sc_h
        return x, y
    except:
        x = random.choice(range(100, 500))
        y = random.choice(range(100, 500))
        return x, y


async def element_offset_value(element):
    bounding_box = await element.bounding_box()
    x = bounding_box['x'] + random.choice(range(1, int(bounding_box['width'])))
    y = bounding_box['y'] + random.choice(range(1, int(bounding_box['height'])))
    return x, y


# 模拟点击
async def elementMouseClick(page, x, y):
    await page.mouse.move(x, y)
    await asyncio.sleep(randomTime(5, 20) / 10)
    await page.mouse.down()
    await asyncio.sleep(randomTime(3, 9) / 10)
    await page.mouse.up()
    await asyncio.sleep(0.5)


# 定义动态线程池
def dynamic_thread_pool():
    cpu_count = psutil.cpu_count()
    # 获取当前系统负载
    load = psutil.getloadavg()
    # 计算可用线程数量
    available_threads = int((cpu_count * 2) - load[0])
    # 确保线程数量不超过最大值
    if available_threads > 10:
        available_threads = 10
    # 若负载较低则逐渐增加线程数
    elif available_threads < 2:
        available_threads = 2
    # 返回动态线程池
    dynamic_thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=available_threads)
    return dynamic_thread_pool._max_workers


def option_init():
    if len(sys.argv) > 1:
        if sys.argv[1] != 'status':
            return
        else:
            menuOptions()


def menuOptions():
    while True:
        # print("================")
        # print("请选择一个选项：")
        # print("================")
        # options = ["1.清空数据库", "2.导入keyword.txt", "x.退出"]
        # input_options = ["1", "2", "x"]
        # while True:
        #     print("\n请选择一个选项：")
        #     for i, option in enumerate(options):
        #         print("{}".format(option))
        #     user_input = input("请输入选项序号：")
        #     if user_input in input_options:
        #         # 如果输入有效，则执行相应的操作
        #         if user_input == "1":
        #             print("清空数据库!!!")
        #             materialDB.MaterialDB().delete_all_data()
        #         elif user_input == "2":
        #             print("导入keyword.txt!!!")
        #             materialDB.MaterialDB().import_data()
        #         elif user_input == 'x':
        #             sys.exit(0)
        #     else:
        #         # print("输入无效，请输入一个有效的选项。")
        #         pass
        pass


def readIniFile(pathfile=''):
    # 判断文件是否存在
    if not os.path.exists(pathfile):
        raise ("读{pathfile}文件错误!")

    with open(pathfile, 'rb') as f:
        content = f.read()
        result = chardet.detect(content)
    iniConfig = configparser.ConfigParser()
    iniConfig.read(pathfile, encoding=result['encoding'])
    return iniConfig


def clearloggin():
    with open(logging_path+loggin_file+".log", 'w') as f:
        f.write('')

 
def cacheIps(_IpsStr='',opt='save'):
    cache = Cache(os.getcwd() + '//data//ips_cache')
    if _IpsStr and opt=='save':
        cache.set("ips",_IpsStr,3600)
        print(cache.get("ips"))
    else:
        return cache.get("ips")